import React from 'react';

export default () => {
  // 1. 交叉类型
  {
    interface A {
      a: string;
    }
    interface B {
      b: string;
    }
    // let data: A & B = { // err 缺少属性 b
    //   a: "on",
    // }
    let data: A & B = {
      a: 'on',
      b: 'yell',
    };
  }
  // 2. 联合类型
  {
    interface A {
      a: string;
    }
    interface B {
      b: string;
    }
    let data: A | B = {
      a: 'on',
    };
    let data2: A | B = {
      b: 'yell',
    };
  }
  // 3. 使用类型断言判断联合类型中确切的类型
  {
    // interface Bird {
    //   fly(): any;
    //   layEggs(): any;
    // }
    //
    // interface Fish {
    //   swim(): any;
    //   layEggs(): any;
    // }
    // function getSmallPet(): Fish | Bird {
    //   // ...
    // }
    //
    // let pet = getSmallPet();
    // if ((pet as Fish).swim) {
    //   (pet as Fish).swim();
    // }
    // else {
    //   (pet as Bird).fly();
    // }
  }
  // 4. 类型谓词
  {
    // interface Bird {
    //   fly(): any;
    //   layEggs(): any;
    // }
    //
    // interface Fish {
    //   swim(): any;
    //   layEggs(): any;
    // }
    // function getSmallPet(): Fish | Bird {
    //   // ...
    // }
    //
    // let pet = getSmallPet();
    //
    // function isFish(pet: Fish | Bird): pet is Fish {
    //   return (pet as Fish).swim !== undefined;
    // }
    //
    // // 注意TypeScript不仅知道在 if分支里 pet是 Fish类型； 它还清楚在 else分支里，一定 不是 Fish类型，一定是 Bird类型。
    // if (isFish(pet)) {
    //   pet.swim();
    // }
    // else {
    //   pet.fly();
    // }
  }
  // 5. typeof 类型保护
  {
    function padLeft(value: string, padding: string | number) {
      if (typeof padding === 'number') {
        return Array(padding + 1).join(' ') + value;
      }
      if (typeof padding === 'string') {
        return padding + value;
      }
      throw new Error(`Expected string or number, got '${padding}'.`);
    }
  }
  // 6. instanceof 保护
  {
    interface Padder {
      getPaddingString(): string;
    }

    class SpaceRepeatingPadder implements Padder {
      constructor(private numSpaces: number) {}
      getPaddingString() {
        return Array(this.numSpaces + 1).join(' ');
      }
    }

    class StringPadder implements Padder {
      constructor(private value: string) {}
      getPaddingString() {
        return this.value;
      }
    }

    function getRandomPadder() {
      return Math.random() < 0.5
        ? new SpaceRepeatingPadder(4)
        : new StringPadder('  ');
    }

    // 类型为SpaceRepeatingPadder | StringPadder
    let padder: Padder = getRandomPadder();

    if (padder instanceof SpaceRepeatingPadder) {
      padder; // 类型细化为'SpaceRepeatingPadder'
    }
    if (padder instanceof StringPadder) {
      padder; // 类型细化为'StringPadder'
    }
  }
  // 8. 使用 ! 去除 null 或 undefined
  {
    function broken(name: string | null): string {
      function postfix(epithet: string) {
        return name.charAt(0) + '.  the ' + epithet; // error, 'name' is possibly null
      }
      name = name || 'Bob';
      return postfix('great');
    }

    function fixed(name: string | null): string {
      function postfix(epithet: string) {
        return name!.charAt(0) + '.  the ' + epithet; // ok
      }
      name = name || 'Bob';
      return postfix('great');
    }
  }
  // 9. 类型别名
  {
    type LinkedList<T> = T & { next: LinkedList<T> };

    interface Person {
      name: string;
    }

    var people: LinkedList<Person>;
    // var s = people.name;
    // var s = people.next.name;
    // var s = people.next.next.name;
    // var s = people.next.next.next.name;
  }
  // 10. 可辨识联合
  {
    interface Square {
      kind: 'square';
      size: number;
    }
    interface Rectangle {
      kind: 'rectangle';
      width: number;
      height: number;
    }
    interface Circle {
      kind: 'circle';
      radius: number;
    }
    type Shape = Square | Rectangle | Circle;
    function area(s: Shape) {
      switch (s.kind) {
        case 'square':
          return s.size * s.size;
        case 'rectangle':
          return s.height * s.width;
        case 'circle':
          return Math.PI * s.radius ** 2;
      }
    }
  }
  // 11. 完整性检查
  {
    interface Square {
      kind: 'square';
      size: number;
    }
    interface Rectangle {
      kind: 'rectangle';
      width: number;
      height: number;
    }
    interface Circle {
      kind: 'circle';
      radius: number;
    }
    type Shape = Square | Rectangle | Circle;
    function assertNever(x: never): never {
      throw new Error('Unexpected object: ' + x);
    }
    function area(s: Shape): number {
      switch (s.kind) {
        case 'square':
          return s.size * s.size;
        case 'rectangle':
          return s.height * s.width;
        case 'circle':
          return Math.PI * s.radius ** 2;
        default:
          return assertNever(s); // error here if there are missing cases
      }
    }
  }
  // 13. 索引类型
  {
    // 指定了 names 是 o 的属性
    // 索引类型查询操作符 keyof T 表示的是已知的公共属性名的联合
    // 索引访问操作符 T[K]，意味着 person['name']具有类型 Person['name']
    function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
      return names.map(n => o[n]);
    }
    interface Person {
      name: string;
      age: number;
    }
    let person: Person = {
      name: 'Jarid',
      age: 35,
    };
    let strings: string[] = pluck(person, ['name']); // ok, ["Jarid"]
    let strings2: (string | number)[] = pluck(person, ['name', 'age']); // ok, ["Jarid", 35]
    console.log(strings);
    console.log(strings2);

    // 索引类型查询操作符 keyof 表示的是已知的公共属性名的联合
    let personProps: keyof Person; // 'name' | 'age'
  }
  // 14. 索引类型和字符串索引签名
  {
    interface Map<T> {
      [key: string]: T;
    }
    let keys: keyof Map<number>; // string
    let value: Map<number>['foo']; // number
  }
  // 15. 映射类型
  {
    interface Person {
      name: string;
      age: number;
    }
    type Partial<T> = {
      [P in keyof T]?: T[P];
    };
    type Readonly<T> = {
      readonly [P in keyof T]: T[P];
    };
    type PersonPartial = Partial<Person>;
    type ReadonlyPerson = Readonly<Person>;

    type Keys = 'option1' | 'option2';
    // 类型变量 K，它会依次绑定到每个属性。
    type Flags = { [K in Keys]: boolean };
    type Flags2 = {
      option1: boolean;
      option2: boolean;
    };

    type NullablePerson = { [P in keyof Person]: Person[P] | null };
    type PartialPerson = { [P in keyof Person]?: Person[P] };
    type Nullable<T> = { [P in keyof T]: T[P] | null };
    type Partial_<T> = { [P in keyof T]?: T[P] };

    type T00 = Exclude<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // "b" | "d"
    type T01 = Extract<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // "a" | "c"

    type T02 = Exclude<string | number | (() => void), Function>; // string | number
    type T03 = Extract<string | number | (() => void), Function>; // () => void

    type T04 = NonNullable<string | number | undefined>; // string | number
    type T05 = NonNullable<(() => string) | string[] | null | undefined>; // (() => string) | string[]

    function f1(s: string) {
      return { a: 1, b: s };
    }

    class C {
      x = 0;
      y = 0;
    }

    type T10 = ReturnType<() => string>; // string
    type T11 = ReturnType<(s: string) => void>; // void
    type T12 = ReturnType<<T>() => T>; // {}
    type T13 = ReturnType<<T extends U, U extends number[]>() => T>; // number[]
    type T14 = ReturnType<typeof f1>; // { a: number, b: string }
    type T15 = ReturnType<any>; // any
    type T16 = ReturnType<never>; // any
    // type T17 = ReturnType<string>;  // Error
    // type T18 = ReturnType<Function>;  // Error

    type T20 = InstanceType<typeof C>; // C
    type T21 = InstanceType<any>; // any
    type T22 = InstanceType<never>; // any
    // type T23 = InstanceType<string>;  // Error
    // type T24 = InstanceType<Function>;  // Error
  }
  return (
    <div>
      <h1>高级类型</h1>
      <p>
        1. 交叉类型：将多个类型合并为一个类型。例如， Person & Serializable &
        Loggable。
      </p>
      <p>
        2. 联合类型：一个值可以是几种类型之一。例如， number | string |
        boolean。
      </p>
      <p>
        3. 联合类型中：想确切地了解是否为某个类型，可以使用类型断言进行判断。
      </p>
      <p>
        4. 类型谓词。 谓词为 parameterName is Type这种形式，
        parameterName必须是来自于当前函数签名里的一个参数名。谓词可以将变量缩减为那个具体的类型，只要这个类型与变量的原始类型是兼容的。
      </p>
      <p>5. typeof 被 ts 识别为类型保护，可以直接在代码里检查类型。</p>
      <p>
        6. instanceof 也被 ts
        识别为类型保护，可以直接在代码里通过构造类型来细化类型。
      </p>
      <p>
        7. ? 表示的可选参数与可选属性，其实就是加上了 undefined 的联合类型。
      </p>
      <p>
        8. 可以使用类型断言手动去除 null 或 undefined。语法是添加 !后缀：
        identifier!从 identifier的类型里去除了 null和 undefined。
      </p>
      <p>9. type 可以定义类型别名，创造一些稀奇古怪的类型。</p>
      <p>
        10.
        可辨识联合，可以联合多个接口定义一个可识别的字符串字面量类型用来判断具体使用的类型接口。
      </p>
      <p>11. 完整性检查。检查可辨识联合中全部不满足返回 undefined 的情况。</p>
      <p>12. 多态的 this类型。this 可以作为类型，代表 this。</p>
      <p>13. 索引类型。编译器能够检查使用了动态属性名的代码。</p>
      <p>14. 索引类型和字符串索引签名。keyof T 必定为 string</p>
      <p>15. 映射类型。</p>
    </div>
  );
};
